package main

import (
	"context"
	carpb "coolcar/v1/car/api/gen/v1"
	"coolcar/v1/car/car"
	"coolcar/v1/car/dao"
	"coolcar/v1/car/mq/amqpclt"
	"coolcar/v1/car/sim"
	"coolcar/v1/car/trip"
	"coolcar/v1/car/ws"
	rentalpb "coolcar/v1/rental/api/gen/v1"
	"coolcar/v1/shared/server"
	"coolcar/v1/shared/zaplog"
	"github.com/gorilla/websocket"
	"github.com/namsral/flag"
	"github.com/streadway/amqp"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"google.golang.org/grpc"
	"log"
	"net/http"
	"time"
)

var (
	mongodbName = "coolcar"
)

var addr = flag.String("addr", ":8084", "address to listen")
var wsAddr = flag.String("ws_addr", ":9090", "websocket address to listen")
var mongoURI = flag.String("mongo_uri", "mongodb://localhost:27017/?readPreference=primary&ssl=false", "mongo uri")
var amqpURI = flag.String("amqp_uri", "amqp://guest:guest@localhost:5672/", "amqp uri")
var carAddr = flag.String("car_addr", ":8084", "car addr")
var tripAddr = flag.String("trip_addr", ":8082", "trip addr")

func main() {
	flag.Parse()
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	sugar, err := zaplog.NewZapLogger()
	if err != nil {
		log.Fatalf("failed to create logger: %v", err)
	}
	defer sugar.Sync()

	mc, err := mongo.Connect(ctx, options.Client().ApplyURI(*mongoURI))
	if err != nil {
		sugar.Fatalf("failed to connect mongodb: %v", err)
	}
	db := mc.Database(mongodbName)

	conn, err := amqp.Dial(*amqpURI)
	if err != nil {
		sugar.Fatalf("failed to connect rabbitmq: %v", err)
	}
	pub, err := amqpclt.NewPublisher(conn, "coolcar")
	if err != nil {
		sugar.Fatalf("failed to create publisher: %v", err)
	}

	carconn, err := grpc.Dial(*carAddr, grpc.WithInsecure())
	if err != nil {
		sugar.Fatalf("failed to connect to grpc: %v", err)
	}

	subscriber, err := amqpclt.NewSubscriber(conn, "coolcar", sugar)
	if err != nil {
		sugar.Fatalf("failed to create subscriber: %v", err)
	}

	simController := &sim.Controller{
		CarService: carpb.NewCarServiceClient(carconn),
		Sugar:      sugar,
		Subscriber: subscriber,
	}
	go simController.RunSimulations(context.Background())

	// Start websocket handler
	u := &websocket.Upgrader{CheckOrigin: func(r *http.Request) bool {
		return true
	}}
	http.HandleFunc("/ws", ws.Handler(u, subscriber, sugar))
	go func() {
		sugar.Info("\tHTTP server started at ", *wsAddr)
		sugar.Fatal(http.ListenAndServe(*wsAddr, nil))
	}()

	// Start trip updater.
	tripConn, err := grpc.Dial(*tripAddr, grpc.WithInsecure())
	if err != nil {
		sugar.Fatalf("failed to connect grpc trip server: %v", err)
	}
	go trip.RunUpdater(subscriber, rentalpb.NewTripServiceClient(tripConn), sugar)

	sugar.Fatal(server.RunGRPCServer(&server.Config{
		Name: "car",
		Addr: *carAddr,
		RegisterFunc: func(g *grpc.Server) {
			carpb.RegisterCarServiceServer(g, &car.Service{
				Mongo:     dao.NewMongo(db),
				Sugar:     sugar,
				Publisher: pub,
			})
		},
		Suger: sugar,
	}))
}
